﻿// -- MVC4WPF
// -- Copyright © 2009 Information Control Corporation
// -- Revision History:
// -- Version 1.0.0.0 Target stable release - Stephen Giffin

using System;
using System.Windows;
using System.ComponentModel;
using System.Reflection;

namespace MVC4WPF
{
    /// <summary>
    /// Helper class that binding data classes that implement the <cref=IDesignLoader> interface, when the system is in design mode.
    /// </summary>
    public class Design
    {
        /// <summary>
        /// Attached property that stores the type of data to be attached to the parents data context. The data type has to support a default constructor, and the <cref=IDesignLoader> interface.
        /// </summary>
        public static readonly DependencyProperty DataContextProperty = DependencyProperty.RegisterAttached(
            "DataContext",
            typeof(Type),
            typeof(Design),
            new UIPropertyMetadata(OnDataContextChanged));

        /// <summary>
        /// Default implementation of the get method for the DataContext dependancy property.
        /// </summary>
        /// <param name="obj">The source dependancy object to get the property from.</param>
        /// <returns>The type of data to be loaded into the datacontext.</returns>
        public static Type GetDataContext(DependencyObject obj)
        {
            return (Type)obj.GetValue(DataContextProperty);
        }

        /// <summary>
        /// Default implementation of the set method for the DataContext dependancy property.
        /// </summary>
        /// <param name="obj">The source dependancy object to set the data on.</param>
        /// <param name="value">A type object that determines which type of data object is to be loaded.</param>
        public static void SetDataContext(DependencyObject obj, Type value)
        {
            obj.SetValue(DataContextProperty, value);
        }

        /// <summary>
        /// Event subscriber that will set the datacontext if the system is in design mode.
        /// </summary>
        /// <param name="obj">The parent of the attached property</param>
        /// <param name="args">The parameter data that has changed</param>
        private static void OnDataContextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {

            //Confirming the system is in design mode.
            if (DesignerProperties.GetIsInDesignMode(obj))
            {

                //Casting the parent type of the attached property to FrameworkElement. This is done to confirm the parent supports a DataContext.
                FrameworkElement parent = obj as FrameworkElement;

                if (parent != null)
                {
                    //Loading the type from the attached property
                    Type dataObject = args.NewValue as Type;

                    //Confirming that a type was passed, otherwise drop out
                    if (dataObject != null)
                    {
                        try
                        {
                            //Creating a empty constructor signature
                            Type[] signature = new Type[0];

                            //Getting the constructor for the data object,
                            ConstructorInfo constructor = dataObject.GetConstructor(signature);

                            //If no default constructor was found exit.
                            if (constructor != null)
                            {
                                //Loading the data class and casting it to IDesignLoader
                                IDesignLoader designData = constructor.Invoke(null) as IDesignLoader;

                                //Confirming the data class supports IdesignLoader, if it doesnt exit since we have no way of loading the default data.
                                if (designData != null)
                                {
                                    //Loading the design data and attaching it to the parent datacontext.
                                    designData.LoadDesignData();
                                    parent.DataContext = designData;
                                }
                            }
                        }
                        catch (System.Exception)
                        {
                            //Add locals support
                        }

                    }
                }
            }
        }
    }
}
